package cn.always.xiajia.framework.security.core.util;

import java.util.Collections;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.springframework.lang.Nullable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;

import cn.always.xiajia.framework.common.web.WebFrameworkUtils;
import cn.always.xiajia.framework.security.core.entity.LoginUser;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

/**
 * 安全服务工具类
 * 
 * @author xgj
 *
 */
public class SecurityFrameworkUtils {

	/**
	 * HEADER 认证头 value 的前缀
	 */
	public static final String AUTHORIZATION_BEARER = "Bearer";

	public static final String REQUEST_ATTRIBUTE_LOGIN_USER = "login_user";

	private SecurityFrameworkUtils() {

	}

	/**
	 * 从请求中，获得认证 Token
	 *
	 * @param request 请求
	 * @param headerName 认证 Token 对应的 Header 名字
	 * @param parameterName 认证 Token 对应的 Parameter 名字（传参中）
	 * @return 认证 Token
	 */
	public static String obtainAuthorization(HttpServletRequest request, String headerName, String parameterName) {
		// 1. 获得 Token。优先级：Header > Parameter
		String token = request.getHeader(headerName);
		// 空回去传参的token
		if (StrUtil.isEmpty(token)) {
			token = request.getParameter(parameterName);
		}
		// 判断是否空格和null和""
		if (!StringUtils.hasText(token)) {
			return null;
		}
		// 2. 去除 Token 中带的 Bearer
		int index = token.indexOf(AUTHORIZATION_BEARER + " ");
		return index >= 0 ? token.substring(index + 7).trim() : token;
	}

	/**
	 * 获得当前认证信息
	 *
	 * @return 认证信息
	 */
	public static Authentication getAuthentication() {
		SecurityContext context = SecurityContextHolder.getContext();
		if (context == null) {
			return null;
		}
		return context.getAuthentication();
	}

	/**
	 * 获取当前用户
	 *
	 * @return 当前用户
	 */
	@Nullable
	public static LoginUser getLoginUser() {
		Authentication authentication = getAuthentication();
		if (authentication == null) {
			return null;
		}
		return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
	}

	/**
	 * 获得当前用户的ID，从上下文中
	 *
	 * @return 用户ID
	 */
	@Nullable
	public static Long getLoginUserId() {
		LoginUser loginUser = getLoginUser();
		return loginUser != null ? loginUser.getUserId() : null;
	}

	/**
	 * 获得当前用户的编号，从上下文中
	 *
	 * @return 用户编号
	 */
	@Nullable
	public static String getLoginUserCode() {
		LoginUser loginUser = getLoginUser();
		return loginUser != null ? loginUser.getUserCode() : null;
	}

	/**
	 * 获得当前用户的名称，从上下文中
	 *
	 * @return 用户名称
	 */
	@Nullable
	public static String getLoginUserName() {
		LoginUser loginUser = getLoginUser();
		return loginUser != null ? loginUser.getUserName() : null;
	}

	/**
	 * 获得当前用户的类型，从上下文中
	 *
	 * @return 用户类型
	 */
	@Nullable
	public static String getLoginUserType() {
		LoginUser loginUser = getLoginUser();
		return loginUser != null ? loginUser.getUserType() : null;
	}

	/**
	 * 设置到request
	 * 
	 * @param request
	 * @param userType 用户类型
	 */
	public static void setRqLoginUser(ServletRequest request, LoginUser loginUser) {
		request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER, JSONUtil.toJsonStr(loginUser));
	}

	/**
	 * 设置当前用户
	 *
	 * @param loginUser 登录用户
	 * @param request 请求
	 */
	public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
		// 创建 Authentication，并设置到上下文
		Authentication authentication = buildAuthentication(loginUser, request);
		SecurityContextHolder.getContext().setAuthentication(authentication);

		// 额外设置到 request 中，用于 ApiAccessLogFilter 可以获取到用户编号；
		// 原因是，Spring Security 的 Filter 在 ApiAccessLogFilter
		// 后面，在它记录访问日志时，线上上下文已经没有用户编号等信息
		WebFrameworkUtils.setRqLoginUserId(request, loginUser.getUserId());
		WebFrameworkUtils.setRqLoginUserCode(request, loginUser.getUserCode());
		WebFrameworkUtils.setRqLoginUserName(request, loginUser.getUserName());
		WebFrameworkUtils.setRqLoginUserType(request, loginUser.getUserType());
	}

	private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
		// 创建 UsernamePasswordAuthenticationToken 对象
		UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null,
				Collections.emptyList());
		authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
		return authenticationToken;
	}

}
